package WizardTD.board;

import WizardTD.App;
import WizardTD.DataConstruct;
import WizardTD.constant.Constant;
import WizardTD.constant.ImageConstant;
import WizardTD.element.Tower;
import WizardTD.enums.ButtonEnum;
import lombok.Data;
import processing.core.PApplet;
import processing.core.PImage;

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@Data
public class Board {
    private PApplet pApplet;

    //board array
    private char[][] array;

    //function button map
    private Map<String, FuncButton> funButtonMap = new HashMap<>();

    private Set<ButtonEnum> choiceButtons = new HashSet<>();

    private ManaProcessBar manaProcessBar = new ManaProcessBar();

    public Board(PApplet pApplet, String layout) {
        this.pApplet = pApplet;
        String[] layoutArray = pApplet.loadStrings(layout);
        int n = layoutArray.length;
        array = new char[n][n];
        // 填充字符二维数组
        for (int i = 0; i < n; i++) {
            String str = layoutArray[i];
            if (str.length() < n) {
                str = String.format("%-" + n + "s", str);
            }
            array[i] = str.toCharArray();
        }

        //构建funcButton
        float count = 2;
        float verticalSpacing = 0.1f;
        float rectX = App.CELLSIZE * 20;
        float rectY = App.CELLSIZE * count;
        FuncButton buttonFF = new FuncButton(rectX, rectY, ButtonEnum.FF);
        count += 2 + verticalSpacing;
        rectY = App.CELLSIZE * count;

        FuncButton buttonP = new FuncButton(rectX, rectY, ButtonEnum.P);
        buttonP.setDoWork(() -> {
            App.isPause = !App.isPause;
        });
        count += 2 + verticalSpacing;
        rectY = App.CELLSIZE * count;

        FuncButton buttonT = new FuncButton(rectX, rectY, ButtonEnum.T);
        count += 2 + verticalSpacing;
        rectY = App.CELLSIZE * count;

        FuncButton buttonU1 = new FuncButton(rectX, rectY, ButtonEnum.U1);
        count += 2 + verticalSpacing;
        rectY = App.CELLSIZE * count;

        FuncButton buttonU2 = new FuncButton(rectX, rectY, ButtonEnum.U2);
        count += 2 + verticalSpacing;
        rectY = App.CELLSIZE * count;

        FuncButton buttonU3 = new FuncButton(rectX, rectY, ButtonEnum.U3);
        count += 2 + verticalSpacing;
        rectY = App.CELLSIZE * count;

        FuncButton buttonM = new FuncButton(rectX, rectY, ButtonEnum.M);
        buttonT.setDoWork(() -> {
            //todo
        });

        funButtonMap.put("FF", buttonFF);
        funButtonMap.put("P", buttonP);
        funButtonMap.put("T", buttonT);
        funButtonMap.put("U1", buttonU1);
        funButtonMap.put("U2", buttonU2);
        funButtonMap.put("U3", buttonU3);
        funButtonMap.put("M", buttonM);

        this.manaProcessBar.setManaCap(Constant.initialManaCap);
        this.manaProcessBar.setCurrentMana(Constant.initialMana);
    }

    /**
     * @param waveNum       波数
     * @param remainingTime 计时
     *                      生成顶部
     */
    public void drawTop(int waveNum, int remainingTime) {
        this.pApplet.background(139, 105, 20);
        this.pApplet.fill(0);
        this.pApplet.textSize(20);
        this.pApplet.text("Wave", 0, App.CELLSIZE);
        this.pApplet.text(waveNum, App.CELLSIZE * 2f, App.CELLSIZE);
        this.pApplet.text("starts: ", App.CELLSIZE * 3, App.CELLSIZE);
        this.pApplet.text(remainingTime, App.CELLSIZE * 5, App.CELLSIZE);
        this.pApplet.text("MANA:", App.CELLSIZE * 11, App.CELLSIZE);
        this.manaProcessBar.draw(this.pApplet);
    }

    /**
     * 画背景
     *
     * @param dataConstruct 数据构造类
     */
    public void drawBoard(DataConstruct dataConstruct) {
        int n = array.length;
        for (int y = 0; y < n; y++) {
            for (int x = 0; x < array[y].length; x++) {
                char c = array[y][x];
                float xPos = x * App.CELLSIZE;
                float yPos = y * App.CELLSIZE + App.TOPBAR;
                if (c == 'W') {
                    pApplet.image(ImageConstant.wizardHouse, xPos, yPos);
                } else if (c == 'S') {
                    pApplet.image(ImageConstant.shrub, xPos, yPos);
                } else if (c == ' ') {
                    pApplet.image(ImageConstant.grass, xPos, yPos);
                } else if (c == 'X') {
                    PImage image = generateImage(dataConstruct, y, x);
                    pApplet.image(image, xPos, yPos);
                }
            }
        }
    }


    /**
     * 生成右侧功能栏
     */
    public void drawRight() {
        funButtonMap.forEach((s, funcButton) -> funcButton.draw(pApplet));
    }


    /**
     * 根据坐标翻转拼接图片
     *
     * @param dataConstruct 数据构造类
     * @param x             横坐标
     * @param y             纵坐标
     * @return 图片
     */
    private PImage generateImage(DataConstruct dataConstruct, int x, int y) {

        if (y <= 0 || y >= 19) {
            return ImageConstant.path0;
        }
        if (x <= 0 || x >= 19) {
            return rotateImageByDegrees(ImageConstant.path0, 90);
        }
        // up       (x-1,y)
        // down     (x+1,y)
        // left     (x,y-1)
        // right    (x,y+1)
        if ((dataConstruct.chars[x - 1][y] == 'X' && dataConstruct.chars[x + 1][y] != 'X' && dataConstruct.chars[x][y - 1] != 'X' && dataConstruct.chars[x][y + 1] != 'X') || (dataConstruct.chars[x - 1][y] != 'X' && dataConstruct.chars[x + 1][y] == 'X' && dataConstruct.chars[x][y - 1] != 'X' && dataConstruct.chars[x][y + 1] != 'X')) {
            return rotateImageByDegrees(ImageConstant.path0, 90);
        } else if ((dataConstruct.chars[x - 1][y] != 'X' && dataConstruct.chars[x + 1][y] != 'X' && dataConstruct.chars[x][y - 1] == 'X' && dataConstruct.chars[x][y + 1] != 'X') || (dataConstruct.chars[x - 1][y] != 'X' && dataConstruct.chars[x + 1][y] != 'X' && dataConstruct.chars[x][y - 1] != 'X' && dataConstruct.chars[x][y + 1] == 'X')) {
            return rotateImageByDegrees(ImageConstant.path0, 0);
        } else if (dataConstruct.chars[x - 1][y] == 'X' && dataConstruct.chars[x + 1][y] == 'X' && dataConstruct.chars[x][y - 1] != 'X' && dataConstruct.chars[x][y + 1] != 'X') {
            return rotateImageByDegrees(ImageConstant.path0, 90);
        } else if (dataConstruct.chars[x - 1][y] == 'X' && dataConstruct.chars[x + 1][y] == 'X' && dataConstruct.chars[x][y - 1] == 'X' && dataConstruct.chars[x][y + 1] != 'X') {
            return rotateImageByDegrees(ImageConstant.path2, 90);
        } else if (dataConstruct.chars[x - 1][y] == 'X' && dataConstruct.chars[x + 1][y] == 'X' && dataConstruct.chars[x][y - 1] != 'X' && dataConstruct.chars[x][y + 1] == 'X') {
            return rotateImageByDegrees(ImageConstant.path2, -90);
        } else if (dataConstruct.chars[x - 1][y] == 'X' && dataConstruct.chars[x + 1][y] != 'X' && dataConstruct.chars[x][y - 1] == 'X' && dataConstruct.chars[x][y + 1] == 'X') {
            return rotateImageByDegrees(ImageConstant.path2, 180);
        } else if (dataConstruct.chars[x - 1][y] != 'X' && dataConstruct.chars[x + 1][y] == 'X' && dataConstruct.chars[x][y - 1] == 'X' && dataConstruct.chars[x][y + 1] == 'X') {
            return rotateImageByDegrees(ImageConstant.path2, 0);
        } else if (dataConstruct.chars[x - 1][y] != 'X' && dataConstruct.chars[x + 1][y] == 'X' && dataConstruct.chars[x][y - 1] == 'X' && dataConstruct.chars[x][y + 1] != 'X') {
            return rotateImageByDegrees(ImageConstant.path1, 0);
        } else if (dataConstruct.chars[x - 1][y] == 'X' && dataConstruct.chars[x + 1][y] == 'X' && dataConstruct.chars[x][y - 1] == 'X' && dataConstruct.chars[x][y + 1] == 'X') {
            return rotateImageByDegrees(ImageConstant.path3, 180);
        } else if (dataConstruct.chars[x - 1][y] == 'X' && dataConstruct.chars[x + 1][y] != 'X' && dataConstruct.chars[x][y - 1] == 'X' && dataConstruct.chars[x][y + 1] != 'X') {
            return rotateImageByDegrees(ImageConstant.path1, 90);
        } else if (dataConstruct.chars[x - 1][y] != 'X' && dataConstruct.chars[x + 1][y] == 'X' && dataConstruct.chars[x][y - 1] != 'X' && dataConstruct.chars[x][y + 1] == 'X') {
            return rotateImageByDegrees(ImageConstant.path1, -90);
        } else if (dataConstruct.chars[x - 1][y] == 'X' && dataConstruct.chars[x + 1][y] != 'X' && dataConstruct.chars[x][y - 1] != 'X' && dataConstruct.chars[x][y + 1] == 'X') {
            return rotateImageByDegrees(ImageConstant.path1, 180);
        }
        return ImageConstant.path0;
    }


    /**
     * Source: https://stackoverflow.com/questions/37758061/rotate-a-buffered-image-in-java
     *
     * @param pimg  The image to be rotated
     * @param angle between 0 and 360 degrees
     * @return the new rotated image
     */
    public PImage rotateImageByDegrees(PImage pimg, double angle) {
        BufferedImage img = (BufferedImage) pimg.getNative();
        double rads = Math.toRadians(angle);
        double sin = Math.abs(Math.sin(rads)), cos = Math.abs(Math.cos(rads));
        int w = img.getWidth();
        int h = img.getHeight();
        int newWidth = (int) Math.floor(w * cos + h * sin);
        int newHeight = (int) Math.floor(h * cos + w * sin);

        PImage result = pApplet.createImage(newWidth, newHeight, App.ARGB);
        //BufferedImage rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
        BufferedImage rotated = (BufferedImage) result.getNative();
        Graphics2D g2d = rotated.createGraphics();
        AffineTransform at = new AffineTransform();
        at.translate((newWidth - w) / 2, (newHeight - h) / 2);

        int x = w / 2;
        int y = h / 2;

        at.rotate(rads, x, y);
        g2d.setTransform(at);
        g2d.drawImage(img, 0, 0, null);
        g2d.dispose();
        for (int i = 0; i < newWidth; i++) {
            for (int j = 0; j < newHeight; j++) {
                result.set(i, j, rotated.getRGB(i, j));
            }
        }

        return result;
    }

    /**
     * 判断是否是草丛
     *
     * @param mouseX 横坐标
     * @param mouseY 纵坐标
     * @param side 点
     * @return 是否是草丛
     */
    public boolean isGrass(float mouseX, float mouseY, float side) {
        if (mouseX > 640  || mouseY > 640) {
            return false;
        }
        int n = array.length;
        for (int y = 0; y < n; y++) {
            for (int x = 0; x < array[y].length; x++) {
                char c = array[y][x];
                float xPos = x * App.CELLSIZE;
                float yPos = y * App.CELLSIZE + App.TOPBAR;
                if (c != ' ') {
                    float s = side / 2;
                    if (mouseX + s > xPos && mouseX - s < xPos + App.CELLSIZE && mouseY + s > yPos && mouseY - s < yPos + App.CELLSIZE) {
                        return false;
                    }
                }
            }
        }
        return true;
    }

    /**
     * 判断是否是按钮
     *
     * @param mouseX 横坐标
     * @param mouseY 纵坐标
     * @return 按钮
     */
    public Tower getTower(float mouseX, float mouseY) {
        Tower choose = null;
        for (Tower tower : App.towers) {
            if (mouseX > tower.getCoordinateX() && mouseX < tower.getCoordinateX() + 32
                    && mouseY > tower.getCoordinateY() && mouseY < tower.getCoordinateY() + 32) {
                tower.isChoose = true;
                choose = tower;
            } else {
                tower.isChoose = false;
            }
        }
        return choose;
    }

    public FuncButton isFuncButton(float mouseX, float mouseY) {
        for (String key : funButtonMap.keySet()) {
            FuncButton t = funButtonMap.get(key);
            if (mouseX > t.getRectX() && mouseX < t.getRectX() + t.getRectWidth()
                    && mouseY > t.getRectY() && mouseY < t.getRectY() + t.getRectHeight()) {
                return t;
            }
        }
        return null;
    }
}
